July 27, 2020
💡 왜 Module을 사용할까?
- 다른 파일에서 재사용이 가능하게 하기 위해
- 전체 코드가 한 파일에 넣기에는 너무 클 때 여러 파일로 나누어서 정리 및 관리하기 위해
# my_module.py
my_module_var = 7
def my_module_func():
return "Hello!"
class MyModuleClass:
pass
module
을 사용할 파일에서import
키워드를 사용하여 불러온 후<module 파일 이름>.<module 안에 작성한 variable, function, class의 이름>
으로 사용Exemple
my_module.my_module_func()
Result
Hello!
from <Module 이름> import <변수/함수/클래스 1>, <변수/함수/클래스 2>...<변수/함수/클래스 n>
from my_module import my_module_func, my_module_var
print(my_module_var)
my_module_func()
from <Module 이름> import *
from <Module 이름> import <변수/함수/클래스> as <사용자 지정 이름>
import <Module 이름> as <사용자 지정 이름>
💡왜 module에 이름을 붙일까?
- 서로 다른 모듈에서 동일한 이름의 변수, 함수, 클래스가 있다면 충돌할 수 있고
- 원하는 요소의 이름이 너무 길수도 있기 때문에
import pkg.mod1
from pkg.mod2 import func2
pkg.mod1.func2()
func2()
__init__.py
파일로 Package의 초기 설정을 해줄 수 있다.__init__.py
의 코드들이 자동으로 실행 된다.__init__.py
파일을 사용할까?# __init__.py
from .mod1 import func2
# main.py
from pkg import func2
# 이미 pkg.mod1에 있는 함수지만 __init__.py에서 이미 경로를 지정했기때문에
# 모듈 파일을 지정하지 않고 사용가능
func2()
Package에서 Import할 수 있는 변수, 함수, 클래스 제한이 가능하다.
__init__.py
파일 내 __all__
변수를 통해 사용할 요소를 지정하여 제한 시킬 수 있다.__all__
변수의 Default 값은 모든 변수,함수,클래스 (때문에 따로 정의를 하여 요소를 제한 시킴)__all__
변수는 String
값의 요소를 갖고 있는 List
(List of String)String
으로 List
에 선언# __init__.py
from .mod1 import func2
from .mod2 import func3
__all__ = ['func2', 'func3'] #__all__ 변수에 사용할 함수 정의
# main.py
from pkg import *
func2()
func3()
func4() ## <== Error. func4 함수는 __all__ 에 정의되지 않았으므로 import 될 수 없음.
💡왜 import 할 수 있는 요소를 제한할까?
pip install Django
sys.modules
은 단순한 dictionary
sys.modules
에서 찾을 수 없다.💡sys.modules에 import된 Module, Package를 확인 하는 방법
import sys
print(sys.modules)
{
'sys': <module 'sys' (built-in)>,
'builtins': <module 'builtins' (built-in)>,
'_frozen_importlib': <module 'importlib._bootstrap' (frozen)>,...
'enum': <module 'enum' from '/usr/lib/python3.8/enum.py'>,
'signal': <module 'signal' from '/usr/lib/python3.8/signal.py'>
}
dictionary
타입임을 확인 할 수 있다.
print()
, set()
과 같은 함수들이 해당된다.List
이며 String
요소들을 갖고 있다.List
의 각 경로를 하나씩 확인해 가면서 해당 경로에 import 한 Package가 위치해 있는지 확인한다.sys.path
에서도 못 찾는다면 Python은 ModuleNotFoundError
에러를 리턴한다.💡sys.path 경로 확인 방법
import sys
print(sys.path)
['/home/runner/Python-Training', '/opt/virtualenvs/python3/lib/pyth
on3.8/site-packages', '/usr/lib/python38.zip', '/usr/lib/python3.8'
, '/usr/lib/python3.8/lib-dynload']
List
타입임을 확인 할 수 있다.
sys.path
에 이미 포함되어 있다.Absolute Path 사용방법
└── my_app
├── main.py
├── package1
│ ├── module1.py
│ └── module2.py
└── package2
├── __init__.py
├── module3.py
├── module4.py
└── subpackage1
└── module5.py
위와 같이 package1
, package2
이라는 두개의 패키지와 package2
와 같이 subpackage1
이라는 중첩 package를 갖고 있는 프로젝트에서 package1
,package2
를 import 한다고 가정할 때 다음과 같이 import 하면 된다.
from package1 import module1
from package1.module2 import function1
from package2 import class1
from package2.subpackage1.module5 import function2
my_app이라는 최상위 directory에서 시작하여 경로를 지정한다. 따라서 subpackage1
의 module5
module의 function2
함수를 import 하기 위해 다음과 같은 경로를 거치게 된다.
my_app ➡️ package2 ➡️ subpackage1 ➡️ module5
만약 Python이 리눅스라면 백슬래시(/) 윈도우라면 슬래시() 형태로 이 경로가 나타날 것이고 Python은 이점(.)을 이용하여 이 경로를 표현한다. 다만 최상위 directory인 my_app은 current directory라고 하는 현재의 프로젝트 디렉토리는 Default로
sys.path
에 포함되어 생략되고 다음과 같이 표현 가능하다.
package2.subpackage1.module5
Relative Path 사용 방법
└── my_app
├── main.py
├── package1
│ ├── module1.py
│ └── module2.py
└── package2
├── __init__.py
├── module3.py
├── module4.py
└── subpackage1
└── module5.py
위와 같이 package1
, package2
이라는 두개의 패키지와 package2
와 같이 subpackage1
이라는 중첩 package를 갖고 있는 프로젝트에서 package2
의 module3
에서 package2
의 class1
과 package2
의 하위 package인 subpackage1
의 module5
의 function2
를 import 한다고 가정할 때 다음과 같이 import 하면 된다.
# package2/module3.py
from .import class1
from .subpackage1.module5 import function2
여기서 점(.)은 import 하는 파일의 현재 위치를 말한다. 즉, 위 예제에서 module3
가 위치해 있는 경로인 package2가 현재 경로, 점(.)인 것이다.
그럼 만약 subpackage1
에서 package2
의 module4
를 import 하려고 한다면 어떻게 해야 할까.
# subpackage1/module5.py
from ..module4 import class4
그럴 때는 상위 디렉토리로 이동해야 하므로 점(.)을 2개 사용하여(..) 상위 디렉토리로 이동해야 한다. (터미널에서 폴더 이동하는 것과 같다)
sys.modules
sys.modules
은 단순한 dictionary
sys.modules
에서 찾을 수 없다.sys.path
List
이며 String
요소들을 갖고 있다.List
의 각 경로를 하나씩 확인해 가면서 해당 경로에 import 한 Package가 위치해 있는지 확인한다.sys.path
에서도 못 찾는다면 Python은 ModuleNotFoundError
에러를 리턴한다.위에 잠깐 정리가 되었지만, sys.modules
를 출력해보면 다음과 같은 결과를 얻을 수 있다.
import sys
print(sys.modules)
{
'sys': <module 'sys' (built-in)>,
'builtins': <module 'builtins' (built-in)>,
'_frozen_importlib': <module 'importlib._bootstrap' (frozen)>,...
'enum': <module 'enum' from '/usr/lib/python3.8/enum.py'>,
'signal': <module 'signal' from '/usr/lib/python3.8/signal.py'>
}
여기서 제일 첫 줄을 보면 'sys': <module 'sys' (built-in)>
임을 확인 할 수 있다. 즉, Python은 sys module을 built-in modules
에서 찾는다는 것을 확인 할 수 있다.
Absolute Path
Relative Path
만약 main 모듈에서 Relative Path로 module를 import하면 다음과 같은 오류가 발생된다.
Attempted relative import beyond top-level package
원인은 Python documents에서 찾을 수 있는데 다음과 같이 안내 하고 있다.
Since the name of the main module is always ”main”, modules intended for use as the main module of a Python application must always use absolute imports.
Python에서 Relative Path는 항상 Module의 이름을 기준으로 경로를 찾는데 main module의 이름은 항상 main이다. 이를 알 수 있는 방법은 __name__
이란 변수를 호출해 보면 알 수 있다.
💡예제 코드의 각 모듈에
__name__
변수를 출력하도록 수정 한 후 실행 해보자.Code
# main.py
# absoulte path
#from calculator.add_and_multiply import add_and_multiply
# relative path
from calculator.add_and_multiply import add_and_multiply
if __name__ == '__main__':
print(add_and_multiply(1, 2))
print(__name__)
# add_and_multiply.py
from .multiplication import multiply
# from calculator.multiplication import multiply
def add_and_multiply(a, b):
print(__name__)
return multiply(a, b) + (a+b)
# multiplication.py
def multiply(a, b):
print(__name__)
return(a*b)
Result
calculator.add_and_multiply #add_and_multiply.py의 __name__
calculator.multiplication #multiplication.py의 __name__
5 #add_and_multiply module에서 리턴된 값
__main__ #main.py의 __name__
따라서 main module에서 Relative Path를 통해 모듈의 경로를 찾을수 없으며 main module에서는 항상 Absolute Path를 사용해야 한다. 따라서 예제 코드를 수정 하면 다음과 같다.
# absoulte path
from calculator.add_and_multiply import add_and_multiply
# relative path
#from .calculator.add_and_multiply import add_and_multiply
if __name__ == '__main__':
print(add_and_multiply(1,2))
from calculator.multiplication import multiply
def add_and_multiply(a, b):
return multiply(a, b) + (a+b)
Python이 Module을 찾는 순서는 다음과 같다.
from .multiplication import multiply
def add_and_multiply(a, b):
return multiply(a, b) + (a+b)
Python이 Module을 찾는 순서는 다음과 같다.
__**init**__.py
의 역할# __init__.py
from .mod1 import func2
# main.py
from pkg import func2
# 이미 pkg.mod1에 있는 함수지만 __init__.py에서 이미 경로를 지정했기때문에
# 모듈 파일을 지정하지 않고 사용가능
func2()
Package에서 Import할 수 있는 변수, 함수, 클래스 제한이 가능하다.
__init__.py
파일 내 __all__
변수를 통해 사용할 요소를 지정하여 제한 시킬 수 있다.__all__
변수의 Default 값은 모든 변수,함수,클래스 (때문에 따로 정의를 하여 요소를 제한 시킴)__all__
변수는 String
값의 요소를 갖고 있는 List
(List of String)String
으로 List
에 선언# __init__.py
from .mod1 import func2
from .mod2 import func3
__all__ = ['func2', 'func3'] #__all__ 변수에 사용할 함수 정의
# main.py
from pkg import *
func2()
func3()
func4() ## <== Error. func4 함수는 __all__ 에 정의되지 않았으므로 import 될 수 없음.